import bytom from './bytom'
import uuid from 'uuid'
import * as Actions from '../store/constants';
import _ from 'lodash'
import {getDomains, camelize} from '@/utils/utils.js'
import * as Sentry from "@sentry/browser";

import { wallet, setChain, setNet } from './wallet'

let account = {
  // setupNet: bytom.setupNet
  setupNet: (net) => {
    // older version compat
    const network = net.substr(0, 7)
    const chain = net.substr(7)
    setNet(network)
    setChain(chain === 'vapor' ? chain : window.store.state.bytom.settings.chainType === 'bytom1' ? 'bytom1' : 'bytom')
    bytom.setupNet(net)
  }
}

function findOrCreateAccount (pubkey, guid, chain) {
  const currentChain = wallet.chain
  setChain(chain)
  // return bytom.wallet.list(pubkey).then(async (wallet) =>{
  return wallet.getWallets(pubkey).then(async (res) =>{
    if (res.length > 0) {
      return {
        guid: res[0].guid,
        address: res[0].addresses ? res[0].addresses[0] : res[0].address
      }
    } else {
      // return bytom.accounts.createNewAccount(pubkey, 'byone')
      return wallet.createAccount(pubkey, guid)
    }
  }).finally(() => {
    setChain(currentChain)
  })
}

function createAccount (pubkey, guid, chain) {
  // bytom.setupNet(`${net}${chain}`)
  // return bytom.accounts.createNewAccount(pubkey, 'byone')
  const currentChain = wallet.chain
  setChain(chain)
  return wallet.createAccount(pubkey, guid).finally(() => {
    setChain(currentChain)
  })
}

account.createKey = function(accountAlias, keyAlias, passwd, context) {
  let retPromise = new Promise((resolve, reject) => {
    if (!keyAlias) {
      keyAlias = `${accountAlias}-key-${uuid.v4()}`
    }

    const _bytom = context.bytom.clone();

    const accountObject = _bytom.keychain.pairs[_bytom.settings.network][accountAlias]
    if(accountObject && accountObject.vMnemonic){
      reject('Alias already exist, please use another alias.')
    }

    _bytom.keychain.removeUnverifyIdentity(_bytom.settings.network);
    _bytom.settings.netType = 'bytom';

    // const resultObj = bytom.keys.createKey(keyAlias, passwd)
    const resultObj = wallet.createKey(keyAlias, passwd)

    resultObj.alias = accountAlias
    resultObj.keyAlias = keyAlias
    resultObj.vMnemonic = false

    context[Actions.SET_MNEMONIC](resultObj['mnemonic']).then(()=>{
      delete resultObj['mnemonic']

      _bytom.currentAccount = resultObj
      context[Actions.UPDATE_STORED_BYTOM](_bytom).then(() => {
        resolve(resultObj)
      }).catch(e => { throw e })
    })
    .catch(error => {
      reject(error)
    })
  })
  return retPromise
}

account.createAccount = function( context) {
  let retPromise = new Promise((resolve, reject) => {
    const _bytom = context.bytom.clone();
    const currentAccount = _bytom.currentAccount
    const keystore = currentAccount.keystore

    createAccount(keystore.xpub, null, 'bytom1').then((bytom1Account) => {
      return createAccount(keystore.xpub, bytom1Account.guid, 'bytom').then(async (bytom2Account) => {
        let resultObj =  Object.assign(currentAccount, bytom2Account)
        resultObj.vMnemonic = true;
        resultObj.bytom1 = bytom1Account
  
        const domains = await getDomains();
        _bytom.settings.domains = Array.from(new Set(_bytom.settings.domains.concat(domains)))
  
        _bytom.keychain.pairs[_bytom.settings.network][currentAccount.alias] = resultObj
        _bytom.currentAccount = resultObj
        
        if (_bytom.settings.chainType === 'bytom2') {
          setChain('bytom')
        } else {
          setChain('bytom1')
        }

        context[Actions.UPDATE_STORED_BYTOM](_bytom).then(() => {
          resolve(resultObj)
        }).catch(e => { throw e })
      })
    })
    // Promise.all([
    //   createAccount(keystore.xpub, 'bytom1')
    // ]).then(async ([bytom2Account, bytom1Account]) => {
    //   let resultObj =  Object.assign(currentAccount, bytom2Account)
    //   resultObj.vMnemonic = true;
    //   resultObj.bytom1 = bytom1Account

    //   const domains = await getDomains();
    //   _bytom.settings.domains = Array.from(new Set(_bytom.settings.domains.concat(domains)))

    //   _bytom.keychain.pairs[_bytom.settings.network][currentAccount.alias] = resultObj
    //   _bytom.currentAccount = resultObj
    //   context[Actions.UPDATE_STORED_BYTOM](_bytom).then(() => {
    //     resolve(resultObj)
    //   }).catch(e => { throw e })
    // })
  })
  return retPromise
}

account.restoreByMnemonic = function(accountAlias, mnemonic, passwd, context) {
  
  let retPromise = new Promise((resolve, reject) => {
    const keyAlias = `${accountAlias}-key-${uuid.v4()}`

    const _bytom = context.bytom.clone();

    const accountObject =  _bytom.keychain.pairs[_bytom.settings.network][accountAlias]
    if(accountObject && accountObject.vMnemonic){
      reject('Alias already exist, please use another alias.')
    }

    _bytom.keychain.removeUnverifyIdentity(_bytom.settings.network);
    _bytom.settings.netType = 'bytom';

    // const res = bytom.keys.restoreFromMnemonic(keyAlias, passwd, mnemonic)
    const res = wallet.restoreFromMnemonic(keyAlias, passwd, mnemonic)

    // findOrCreateAccount(res.xpub, null, 'bytom1'),
    findOrCreateAccount(res.xpub, null, 'bytom1').then((bytom1Account) => {
      findOrCreateAccount(res.xpub, bytom1Account.guid, 'bytom').then(async (bytom2Account) => {
        const domains = await getDomains();
        _bytom.settings.domains = Array.from(new Set(_bytom.settings.domains.concat(domains)))
        
        let resultObj =  Object.assign(res, bytom2Account)
        resultObj.alias = accountAlias
        resultObj.keyAlias = keyAlias
        resultObj.vMnemonic = true
        resultObj.bytom1 = bytom1Account

        _bytom.keychain.pairs[_bytom.settings.network][accountAlias] = resultObj
        _bytom.currentAccount = resultObj

        if (_bytom.settings.chainType === 'bytom2') {
          setChain('bytom')
        } else {
          setChain('bytom1')
        }

        context[Actions.UPDATE_STORED_BYTOM](_bytom).then(() => {
          resolve(bytom2Account)
        })
        .catch(error => {
          reject(error)
        })
      })
    })
  })
  return retPromise
}

account.restoreByKeystore = function(accountAlias, keystore, password, context) {
  let retPromise = new Promise((resolve, reject) => {

    const _bytom = context.bytom.clone();

    const accountObject =  _bytom.keychain.pairs[_bytom.settings.network][accountAlias]
    if(accountObject && accountObject.vMnemonic){
      reject('Alias already exist, please use another alias.')
    }

    _bytom.keychain.removeUnverifyIdentity(_bytom.settings.network);
    _bytom.settings.netType = 'bytom';

    // older keystore compat
    if (keystore.key_images && keystore.key_images.xkeys) {
      keystore = keystore.key_images.xkeys[0]
    }

    // const res = bytom.keys.restoreFromKeystore(password, keystore)
    const res = wallet.restoreFromKeystore(password, keystore)
    findOrCreateAccount(res.xpub, null, 'bytom1').then((bytom1Account) => {
      findOrCreateAccount(res.xpub, bytom1Account.guid, 'bytom').then(async (bytom2Account) => {
        const domains = await getDomains();
        _bytom.settings.domains = Array.from(new Set(_bytom.settings.domains.concat(domains)))
        
        let resultObj =  Object.assign(bytom2Account, {})
        resultObj.alias = accountAlias
        resultObj.keyAlias = res.keyAlias
        resultObj.vMnemonic = true
        resultObj.keystore = keystore
        resultObj.xpub = res.xpub
        resultObj.bytom1 = bytom1Account

        _bytom.keychain.pairs[_bytom.settings.network][accountAlias] = resultObj
        _bytom.currentAccount = resultObj

        if (_bytom.settings.chainType === 'bytom2') {
          setChain('bytom')
        } else {
          setChain('bytom1')
        }
        
        context[Actions.UPDATE_STORED_BYTOM](_bytom).then(() => {
          resolve(bytom2Account)
        })
          .catch(error => {
            reject(error)
          })
      })
    })
  })
  return retPromise
}

account.copy = function(guid) {
  return wallet.copyToVapor(guid, 'btm')
  // let retPromise = new Promise((resolve, reject) => {
  //   bytom.accounts
  //     .copyAccountUseServer(guid, 'btm')
  //     .then(ret => {
  //       resolve(ret)
  //     })
  //     .catch(error => {
  //       reject(error)
  //     })
  // })
  // return retPromise
}

// account.listVapor = function(guid) {
//   let retPromise = new Promise((resolve, reject) => {
//     bytom.accounts
//       .listVaporAccountUseServer(guid)
//       .then(ret => {
//         resolve(ret)
//       })
//       .catch(error => {
//         reject(error)
//       })
//   })
//   return retPromise
// }

account.balance = function(address , context) {
  let retPromise = new Promise((resolve, reject) => {
    wallet.getBalances(address)
    // bytom.accounts
      // .listAddressUseServer(address)
      .then(address => {
        const _bytom = context.bytom.clone();
        const isVapor = _bytom.settings.netType === 'vapor'
        const isBytom1 = _bytom.settings.chainType === 'bytom1'

        // if (address.address !== context.bytom.currentAccount[isVapor ? 'vpAddress' : 'address']) {
        //   return
        // }
        const currentAddress = isVapor ? _bytom.currentAccount.vpAddress : isBytom1 ? _bytom.currentAccount.bytom1.address : _bytom.currentAccount.address
        if (address.address !== currentAddress) {
          return
        }

        let balances = address.balances || []
        let votes = address.votes || []

        const currentBalance = isVapor? _bytom.currentAccount.vpBalances : isBytom1 ? _bytom.currentAccount.bytom1.balances : _bytom.currentAccount.balances
        const currentVotes = isBytom1 ? _bytom.currentAccount.bytom1.votes : _bytom.currentAccount.votes

          Sentry.configureScope(function(scope) {
            if(!isVapor) {
              scope.setTag("wallet.address", address.address);
            }
          });

        const balanceNotEqual = !_.isEqual(currentBalance, balances)
        const voteNotEqual = !_.isEqual(currentVotes, votes)

        if(balanceNotEqual || voteNotEqual) {
          //update AccountList

          if (balanceNotEqual) {
            if (isVapor) {
              _bytom.currentAccount.vpBalances = balances;
               _bytom.keychain.pairs[_bytom.settings.network][_bytom.currentAccount.alias].vpBalances =balances
            } else if (isBytom1) {
              _bytom.currentAccount.bytom1.balances = balances
               _bytom.keychain.pairs[_bytom.settings.network][_bytom.currentAccount.alias].bytom1.balances = balances
            } else {
              _bytom.currentAccount.balances = balances;
              _bytom.keychain.pairs[_bytom.settings.network][_bytom.currentAccount.alias].balances = balances
            }
          }

          if (voteNotEqual) {
            if (isBytom1) {
              _bytom.currentAccount.bytom1.votes = votes;
             _bytom.keychain.pairs[_bytom.settings.network][_bytom.currentAccount.alias].bytom1.votes = votes
            } else {
              _bytom.currentAccount.votes = votes;
             _bytom.keychain.pairs[_bytom.settings.network][_bytom.currentAccount.alias].votes = votes
            }
          }

          context[Actions.UPDATE_STORED_BYTOM](_bytom)
        }

        resolve()
      })
      .catch(error => {
        reject(error)
      })
  })
  return retPromise
}

// account.list = function() {
//   return  bytom.accounts
//       .listAccountUseServer()
// }

account.listKeyByXpubOld = function(xpub){
  return  bytom.keys
    .getKeyByXPub(xpub)
}

// account.backup = function() {
//   return bytom.wallet.backup()
// }

account.isValidMnemonic = function(mnemonic) {
  // return bytom.keys.isValidMnemonic(mnemonic)
  return wallet.isValidMnemonic(mnemonic)
}

account.isValidKeystore = function(keystore, context) {
  const walletImage = camelize(JSON.parse(keystore));
  //bycoin && bytom wallet
  if(walletImage.keyImages && walletImage.keyImages.xkeys ){
    const keys = walletImage.keyImages.xkeys;
    if(keys.length>1){
      throw(context.$t('error.BTM0010'))
    }else if(keys.length===0){
      throw(context.$t('error.BTM0011'))
    }else{
      const key = keys[0]
      const xpub = key.xpub
      if(context.bytom.keychain.findIdentity(xpub, context.bytom.settings.network)){
        throw(context.$t('error.BTM0012'))
      }else{
        return key
      }
    }
  }
  //V2
  else if(walletImage['accounts-server']){
    const account = walletImage['accounts-server'].filter(a => a.net === context.net)
    if(account.length>1){
      throw(context.$t('error.BTM0010'))
    }else if(account.length===0){
      throw(context.$t('error.BTM0011'))
    }else{
      const xpub = account[0].rootXPub
      if(context.bytom.keychain.findIdentity(xpub, context.bytom.settings.network)){
        throw(context.$t('error.BTM0012'))
      }else{
        const key = walletImage["keys"].find(key => key.xpub === xpub)
        return JSON.parse(key.key)
      }
    }
  }
  //invalid format
  else if(!walletImage['crypto']){
    throw(context.$t('error.BTM0011'))
  }
  //existed keystore
  else if(context.bytom.keychain.findIdentity(walletImage.xpub, context.bytom.settings.network)){
    throw(context.$t('error.BTM0012'))
  }
  return walletImage
}

account.isAliasValid = function(alias, context){

  const hanArray = (alias.match(/[\u3000\u3400-\u4DBF\u4E00-\u9FFF]+/g) || []).join('')
  if(hanArray.length> 7){
    throw(context.$t('error.BTM0013'))
  }else if( alias.length >9 ){
    throw(context.$t('error.BTM0014'))
  }
}

account.decryptMnemonic = function(vault,password, context) {
  const keystore = context.bytom.currentAccount.keystore;
  // return bytom.keys.decryptMnemonic(vault, password, keystore)
  return wallet.decryptMnemonic(vault, password, keystore)
}

account.createOld = function(accountAlias, keyAlias, passwd, success, error) {
  let retPromise = new Promise((resolve, reject) => {
    if(!keyAlias){
      keyAlias = `${accountAlias}-key-${uuid.v4()}`
    }
    bytom.keys
      .create(keyAlias, passwd)
      .then(res => {
        bytom.accounts
          .createAccountUseServer(res.xpub, accountAlias)
          .then(ret => {
            resolve(ret)
          })
          .catch(error => {
            reject(error)
          })
      })
      .catch(error => {
        reject(error)
      })
  })
  return retPromise
}

account.isValidPassword = function(keystore, password){
  // return bytom.keys.verifyPassword(keystore, password)
  return wallet.isValidPassword(password, keystore)
}

export default account
